package com.interview.lock;

import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 锁性能对比演示
 * 
 * 对比三种不同的锁策略：
 * 1. synchronized关键字（传统方式）
 * 2. ReentrantLock（显式锁）
 * 3. ConcurrentLinkedQueue（无锁队列）- 推荐方案
 * 
 * 通过实际测试展示为什么ConcurrentLinkedQueue最适合多生产者单消费者场景
 */
public class LockPerformanceComparison {
    
    private static final int PRODUCER_COUNT = 10000;
    private static final int MESSAGES_PER_PRODUCER = 10;
    private static final int THREAD_POOL_SIZE = 100;
    
    public static void main(String[] args) {
        System.out.println("=== 锁性能对比测试 ===");
        System.out.println("生产者数量: " + PRODUCER_COUNT);
        System.out.println("每个生产者消息数: " + MESSAGES_PER_PRODUCER);
        System.out.println("总消息数: " + (PRODUCER_COUNT * MESSAGES_PER_PRODUCER));
        System.out.println("====================\n");
        
        // 测试1：使用synchronized
        System.out.println("【测试1】使用synchronized关键字");
        testSynchronized();
        
        System.out.println("\n" + "=".repeat(50) + "\n");
        
        // 测试2：使用ReentrantLock
        System.out.println("【测试2】使用ReentrantLock");
        testReentrantLock();
        
        System.out.println("\n" + "=".repeat(50) + "\n");
        
        // 测试3：使用ConcurrentLinkedQueue（推荐方案）
        System.out.println("【测试3】使用ConcurrentLinkedQueue（推荐方案）");
        testConcurrentQueue();
        
        System.out.println("\n" + "=".repeat(50) + "\n");
        System.out.println("测试完成！");
        System.out.println("\n结论：");
        System.out.println("在多生产者单消费者场景下，ConcurrentLinkedQueue性能最优，");
        System.out.println("因为生产者无需竞争锁，使用CAS操作实现无锁并发。");
    }
    
    /**
     * 测试1：使用synchronized关键字
     */
    private static void testSynchronized() {
        long startTime = System.currentTimeMillis();
        
        SynchronizedQueue queue = new SynchronizedQueue();
        ExecutorService executorService = Executors.newFixedThreadPool(THREAD_POOL_SIZE);
        
        // 提交生产者任务
        for (int i = 0; i < PRODUCER_COUNT; i++) {
            final int producerId = i;
            executorService.submit(() -> {
                for (int j = 0; j < MESSAGES_PER_PRODUCER; j++) {
                    queue.offer(new Message(producerId * 1000L + j, producerId, "msg"));
                }
            });
        }
        
        executorService.shutdown();
        try {
            executorService.awaitTermination(5, TimeUnit.MINUTES);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
        long endTime = System.currentTimeMillis();
        long duration = endTime - startTime;
        
        System.out.println("耗时: " + duration + " 毫秒");
        System.out.println("队列大小: " + queue.size());
        System.out.printf("吞吐量: %.2f 条/秒%n", 
            (PRODUCER_COUNT * MESSAGES_PER_PRODUCER * 1000.0) / duration);
    }
    
    /**
     * 测试2：使用ReentrantLock
     */
    private static void testReentrantLock() {
        long startTime = System.currentTimeMillis();
        
        ReentrantLockQueue queue = new ReentrantLockQueue();
        ExecutorService executorService = Executors.newFixedThreadPool(THREAD_POOL_SIZE);
        
        // 提交生产者任务
        for (int i = 0; i < PRODUCER_COUNT; i++) {
            final int producerId = i;
            executorService.submit(() -> {
                for (int j = 0; j < MESSAGES_PER_PRODUCER; j++) {
                    queue.offer(new Message(producerId * 1000L + j, producerId, "msg"));
                }
            });
        }
        
        executorService.shutdown();
        try {
            executorService.awaitTermination(5, TimeUnit.MINUTES);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
        long endTime = System.currentTimeMillis();
        long duration = endTime - startTime;
        
        System.out.println("耗时: " + duration + " 毫秒");
        System.out.println("队列大小: " + queue.size());
        System.out.printf("吞吐量: %.2f 条/秒%n", 
            (PRODUCER_COUNT * MESSAGES_PER_PRODUCER * 1000.0) / duration);
    }
    
    /**
     * 测试3：使用ConcurrentLinkedQueue
     */
    private static void testConcurrentQueue() {
        long startTime = System.currentTimeMillis();
        
        MessageQueue queue = new MessageQueue();
        ExecutorService executorService = Executors.newFixedThreadPool(THREAD_POOL_SIZE);
        
        // 提交生产者任务
        for (int i = 0; i < PRODUCER_COUNT; i++) {
            final int producerId = i;
            executorService.submit(() -> {
                for (int j = 0; j < MESSAGES_PER_PRODUCER; j++) {
                    queue.offer(new Message(producerId * 1000L + j, producerId, "msg"));
                }
            });
        }
        
        executorService.shutdown();
        try {
            executorService.awaitTermination(5, TimeUnit.MINUTES);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
        long endTime = System.currentTimeMillis();
        long duration = endTime - startTime;
        
        System.out.println("耗时: " + duration + " 毫秒");
        System.out.println("队列大小: " + queue.size());
        System.out.printf("吞吐量: %.2f 条/秒%n", 
            (PRODUCER_COUNT * MESSAGES_PER_PRODUCER * 1000.0) / duration);
    }
    
    /**
     * 使用synchronized的队列实现
     */
    static class SynchronizedQueue {
        private final Queue<Message> queue = new LinkedList<>();
        
        public synchronized void offer(Message message) {
            queue.offer(message);
        }
        
        public synchronized Message poll() {
            return queue.poll();
        }
        
        public synchronized int size() {
            return queue.size();
        }
    }
    
    /**
     * 使用ReentrantLock的队列实现
     */
    static class ReentrantLockQueue {
        private final Queue<Message> queue = new LinkedList<>();
        private final ReentrantLock lock = new ReentrantLock();
        
        public void offer(Message message) {
            lock.lock();
            try {
                queue.offer(message);
            } finally {
                lock.unlock();
            }
        }
        
        public Message poll() {
            lock.lock();
            try {
                return queue.poll();
            } finally {
                lock.unlock();
            }
        }
        
        public int size() {
            lock.lock();
            try {
                return queue.size();
            } finally {
                lock.unlock();
            }
        }
    }
}

